/*!
 * \file smtp_core.c
 *
 * \brief 共通定义
 *
 * All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.\n
 * 共通使用的宏和结构体
 *
 * \version 1.0
 * \author jikw
 * \date 2015/07/28
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "frreport_comm.h"
#include "frct_comm.h"
#include "frbase64.h"
#include "frservice_comm.h"
#include "config.h"
#include "smtp_core.h"

extern void *g_p_apm_context;

#define  SMTP_REQ_TYPE  "EHLO MAIL RCPT DATA RSET SEND SOML SAML VRFY EXPN HELP NOOP TURN AUTH"   //SMTP命令
#define  SMTP_REQ_DATA_END  "\r\n.\r\n"              //命令DATA之后的数据以"\r\n.\r\n" 结束

typedef struct __st_L7session_smtp_private
{
    unsigned long ul_seq;                             //序列号，1次req和response时，使用同一个序列号
    L4_transaction st_l4_trans;                       //数据
    L7_transaction st_l7_trans;                       //数据
    int i_flag_data_type;                             //1: FRLOGIN_START 2:FRLOGIN_SUCC 3:FRLOGIN_FAIL 4 FRLOGOUT_START 5 FRLOGOUT_END 0 : other
    char *p_header_name;
    Frreport_body_data *p_st_body_data_req;
    Frreport_body_data *p_st_body_data_resp;
    Frlist_header *p_lst_comm;
    Frlist_header *p_lst_req;
    char *p_user_name;
    struct timeval st_login_timestamp;
    char arr_session_id[APM_UUID_SIZE];
    int i_flag_res;                                   //标志位 1||2, 1:上一条resp参数为Username: 2:上一条resp参数为Password:
    int i_flag_response;                              //标志位 0||1, 0:response的code和string不必上报 1：需要上报
    int i_count;                                      //RCPT TO, 从1开始计数
    int i_flag_mail;
    int i_flag_data;
}L7session_smtp_private;

/*
 * smtp私有数据创建
 *
 * \param void
 *
 * \return NULL          内存分配失败\n
 *         p_st_private  内存分配成功
 *
 * \note
 *
 */
FR_STATIC L7session_smtp_private* __smtp_private_create(void)
{
    L7session_smtp_private *p_st_private = NULL;

    p_st_private = (L7session_smtp_private*)frmalloc(sizeof(L7session_smtp_private));
    if (p_st_private == NULL)
        return NULL;

    memset(p_st_private, 0x00, sizeof(L7session_smtp_private));
    return p_st_private;
}

/*!
 * 私有信息重置
 *
 * \param p_private_session  (smtp的私有session数据)
 *
 * \return FR_SUCC   成功\n
 *
 * \note
 *
 */
FR_STATIC int __smtp_session_reset(void *p_private_session)
{
    L7session_smtp_private *p_st_private = (L7session_smtp_private*)p_private_session;
    if (p_st_private)
    {
        if (p_st_private->p_st_body_data_req)
            frreport_body_free(&p_st_private->p_st_body_data_req);

        if (p_st_private->p_st_body_data_resp)
            frreport_body_free(&p_st_private->p_st_body_data_resp);

        if (p_st_private->p_lst_req)
            frreport_body_l7list_free(&p_st_private->p_lst_req);

        if (p_st_private->p_lst_comm)
            frreport_body_l7list_free(&p_st_private->p_lst_comm);

        if (p_st_private->p_header_name)
        {
            frfree(p_st_private->p_header_name);
            p_st_private->p_header_name = NULL;
        }

        memset(&p_st_private->st_l4_trans, 0x00, sizeof(L4_transaction));
        p_st_private->i_flag_data_type = 0;
        p_st_private->i_flag_res = 0;
    }
    return FR_SUCC;
}

/*!
 * 判断是否是smtp协议
 *
 * \param p_st_conn      (输入数据信息)
 * \param p_st_packet    (输入数据)
 *
 * \return APM_L7_IS_YES  属于smtp协议\n
 *         APM_L7_IS_NO   不属于smtp协议
 *
 * \note
 *
 */
int smtp_detect(Conntrack *p_st_conn, Apm_packet *p_st_packet)
{
    int i_ret = 0;
    char *p_data = NULL;
    int i_ret_fun = 0;
    char arr_name[5];

    if (p_st_packet == NULL || p_st_packet->p_payload == NULL || p_st_packet->ui_payload_len <= 3 || p_st_conn==NULL)
        return APM_L7_IS_NO;

    snprintf(arr_name, sizeof(arr_name), "%s", p_st_packet->p_payload);

    p_data = frstrdup(SMTP_REQ_TYPE);
    if (p_data == NULL)
        return APM_L7_IS_NO;
    if (frmemstr(p_data, strlen(p_data), arr_name, 4) == NULL)
    {
        frfree(p_data);
        goto error;
    }

    i_ret_fun = frct_get_direct_known(p_st_conn);
    if (i_ret_fun == FR_DIRECT_UNKNOWN)
        frcap_set_cs_srv(p_st_conn, p_st_packet->dst_ip, p_st_packet->dst_port);

    frfree(p_data);
    frservice_add(g_p_apm_context, MODULE_L7PROTO, p_st_packet->dst_ip, p_st_packet->dst_port, NULL, 0);
    return APM_L7_IS_YES;

error:
    if (frct_get_direct_known(p_st_conn) == FR_DIRECT_UNKNOWN)
    {
        i_ret = frservice_get(g_p_apm_context, MODULE_L7PROTO, p_st_packet->dst_ip, p_st_packet->dst_port, NULL, 0, NULL);
        if (i_ret == FR_SUCC)
        {
            frcap_set_cs_srv(p_st_conn, p_st_packet->dst_ip, p_st_packet->dst_port);
            return APM_L7_IS_YES;
        }
        i_ret = frservice_get(g_p_apm_context, MODULE_L7PROTO, p_st_packet->src_ip, p_st_packet->src_port, NULL, 0, NULL);
        if (i_ret == FR_SUCC)
        {
            frcap_set_cs_srv(p_st_conn, p_st_packet->src_ip, p_st_packet->src_port);
            return APM_L7_IS_YES;
        }
    }
    else
    {
        i_ret = frservice_get(g_p_apm_context, MODULE_L7PROTO, p_st_packet->src_ip, p_st_packet->src_port, NULL, 0, NULL);
        if (i_ret == FR_SUCC)
            return APM_L7_IS_YES;

        i_ret = frservice_get(g_p_apm_context, MODULE_L7PROTO, p_st_packet->dst_ip, p_st_packet->dst_port, NULL, 0, NULL);
        if (i_ret == FR_SUCC)
            return APM_L7_IS_YES;
    }
    return APM_L7_IS_NO;
}

/*!
 * req数据解析
 *
 * \param p_data          (req数据)
 * \param ui_data_len     (req数据长度)
 * \param p_st_private    (私有数据)
 * \param p_st_body_data
 * \param p_st_conn       (入力链接信息)
 *
 * \return APM_L7_ANALYZE_SUCC   成功\n
 *         APM_L7_ANALYZE_FAIL   失败\n
 *         APM_L7_ANALYZE_TODO   未结束
 *
 * \note
 *
 */
FR_STATIC int __req_parse(char *p_data, unsigned int ui_data_len, L7session_smtp_private *p_st_private, Frreport_body_data *p_st_body_data, Conntrack *p_st_conn)
{
    int i_out = 0;
    unsigned int ui_len_tmp = 0;
    char *p_value = NULL;
    char *p_value_key = NULL;
    char *p_pos = NULL;
    char arr_name[64];

    if (p_data == NULL || ui_data_len == 0 || p_st_private == NULL || p_st_body_data == NULL || p_st_conn == NULL)
        return APM_L7_ANALYZE_FAIL;

    if (p_st_private->i_flag_mail == 1)
    {
        if (strncasecmp(p_data, "DATA", 4) == 0)
            p_st_private->i_flag_data = 1;
        if (strncasecmp(p_data, "RCPT TO:", strlen("RCPT TO:")) == 0)
        {
            //p_st_private->i_flag_response = 1;
            //ui_data_len-8-2理解为 总长度-前八个字符-末尾\r\n
            ui_len_tmp =  ui_data_len - strlen("RCPT TO:") - strlen("\r\n") + 1;
            p_value = (char*)frmalloc(ui_len_tmp);
            if (p_value == NULL)
                goto ERROR;
            snprintf(p_value,ui_len_tmp, "%s", p_data+strlen("RCPT TO:"));
            //去除前后的空格/tab
            frtrim(p_value);

            snprintf(arr_name, sizeof(arr_name), "request.smtp.to.%d", ++p_st_private->i_count);
            if (frreport_body_l7_add_str(p_st_body_data, arr_name, p_value) == FR_FAIL)
                goto ERROR;
            frfree(p_value);
            p_value = NULL;
        }
        if (strncmp(p_data+ui_data_len-5, SMTP_REQ_DATA_END, strlen(SMTP_REQ_DATA_END)) == 0)
        {
            if (frreport_body_l7_add_int(p_st_body_data, "request.smtp.to.size", p_st_private->i_count) == FR_FAIL)
                goto ERROR;
            return APM_L7_ANALYZE_SET_REQ(APM_L7_ANALYZE_SUCC);
        }
        return APM_L7_ANALYZE_TODO;
    }

    //查找request的第一个空格
    p_pos = (char*)memchr(p_data, ' ', ui_data_len);
    if (p_pos == NULL)
    {
        //上一响应为经BASE64加密的帐户名
        if (p_st_private->i_flag_res == 1)
        {
            p_value = (char*)frmalloc(ui_data_len-1);
            if (p_value == NULL)
                goto ERROR;
            snprintf(p_value,ui_data_len-1, "%s", p_data);
            p_value_key = (char *)frbase64_decode(p_value, &i_out);
            if (p_st_private->p_user_name)
                frfree(p_st_private->p_user_name);
            p_st_private->p_user_name = p_value_key;
            p_value_key = NULL;
            frfree(p_value);
            p_value = NULL;

            return APM_L7_ANALYZE_TODO;
        }
        //上一响应为经BASE64加密的密码
        if (p_st_private->i_flag_res == 2)
            return APM_L7_ANALYZE_SET_REQ(APM_L7_ANALYZE_SUCC);
        if (strncasecmp(p_data, "QUIT", strlen("QUIT")) == 0)
            p_st_private->i_flag_data_type = 4;
        return APM_L7_ANALYZE_SET_REQ(APM_L7_ANALYZE_SUCC);
    }
    else
    {
        if (strncasecmp(p_data, "AUTH ", strlen("AUTH ")) == 0)
        {
            p_st_private->i_flag_data_type = 1;
            fruuid(p_st_private->arr_session_id);
            p_st_private->st_login_timestamp.tv_sec = p_st_private->st_l4_trans.st_request_time_first_byte.tv_sec;
            p_st_private->st_login_timestamp.tv_usec = p_st_private->st_l4_trans.st_request_time_first_byte.tv_usec;
            return APM_L7_ANALYZE_TODO;
        }
        if (strncasecmp(p_data, "MAIL FROM:", strlen("MAIL FROM:")) == 0)
        {
            p_st_private->i_flag_mail = 1;
            p_st_private->i_flag_response = 1;
            //ui_data_len-10-2理解为 总长度-前十个字符-末尾\r\n
            ui_len_tmp =  ui_data_len - strlen("MAIL FROM:") - strlen("\r\n") + 1;
            p_value = (char*)frmalloc(ui_len_tmp);
            if (p_value == NULL)
                goto ERROR;
            snprintf(p_value,ui_len_tmp, "%s", p_data+strlen("MAIL FROM:"));
            //去除前后的空格/tab
            frtrim(p_value);

            //MAIL FROM: <1041925033@qq.com> SIZE=1335     p_value"<1041925033@qq.com> SIZE=1335"只取邮箱名
            p_pos = (char*)memchr(p_value, ' ', ui_len_tmp);
            if (p_pos != NULL)
                *p_pos = '\0';

            if (frreport_body_l7_add_str(p_st_body_data, "request.smtp.from", p_value) == FR_FAIL)
                goto ERROR;
            snprintf(arr_name, sizeof(arr_name), "%s%s", "MAIL FROM ", p_value);
            if (frreport_body_l7_add_str(p_st_body_data, APM_REPORT_KEY_TRANS_TITLE, arr_name) == FR_FAIL)
                goto ERROR;
            frfree(p_value);
            p_value = NULL;
            return APM_L7_ANALYZE_TODO;
        }
        return APM_L7_ANALYZE_SET_REQ(APM_L7_ANALYZE_SUCC);
    }

ERROR:
    if (p_value)
        frfree(p_value);
    if (p_value_key)
        frfree(p_value_key);

    return APM_L7_ANALYZE_FAIL;

}

/*!
 * resp数据解析
 *
 * \param p_data          (resp数据)
 * \param ui_data_len     (resp数据长度)
 * \param p_st_private    (私有数据)
 * \param p_st_body_data
 * \param p_st_conn       (入力链接信息)
 *
 * \return APM_L7_ANALYZE_SUCC   成功\n
 *         APM_L7_ANALYZE_FAIL   失败\n
 *         APM_L7_ANALYZE_TODO   未结束
 *
 * \note
 *
 */
FR_STATIC int __resp_parse(char *p_data, unsigned int ui_data_len, L7session_smtp_private *p_st_private, Frreport_body_data *p_st_body_data, Conntrack *p_st_conn)
{
    char *p_header_value = NULL;
    char *p_temp = NULL;
    char arr_resp_code[4];

    if (p_data == NULL || ui_data_len == 0 || p_st_private == NULL || p_st_body_data == NULL || p_st_conn == NULL)
        return APM_L7_ANALYZE_FAIL;

    //判断是否为session数据
    if (strncmp(p_data, "221 ", 4) == 0)
        p_st_private->i_flag_data_type = 5;
    if (p_st_private->i_flag_data_type == 1)
    {
        if (strncmp(p_data, "235 ", 4) == 0)
            p_st_private->i_flag_data_type = 2;
        else if (strncmp(p_data, "535 ", 4) == 0)
           p_st_private->i_flag_data_type = 3;
    }
    if (p_st_private->i_flag_data_type == 1)
    {
        //-2理解为去除\r\n
        if (ui_data_len - 2 < strlen("334 dXNlcm5hbWU6"))
            return APM_L7_ANALYZE_FAIL;
        if (strncasecmp(p_data, "334 VXNlcm5hbWU6", ui_data_len-2) == 0 || strncasecmp(p_data, "334 dXNlcm5hbWU6", ui_data_len-2) == 0)
            p_st_private->i_flag_res = 1;
        if (strncasecmp(p_data, "334 UGFzc3dvcmQ6", ui_data_len-2) == 0 || strncasecmp(p_data, "334 cGFzc3dvcmQ6", ui_data_len-2) == 0)
            p_st_private->i_flag_res = 2;
        return APM_L7_ANALYZE_TODO;
    }

    if (p_st_private->i_flag_mail == 1)
    {
        if (p_st_private->i_flag_response == 1)
        {
            snprintf(arr_resp_code, sizeof(arr_resp_code), "%s", p_data);
            if (frreport_body_l7_add_str(p_st_body_data, APM_REPORT_KEY_RESP_CODE, arr_resp_code) == FR_FAIL)
                goto ERROR;
            //之前设置CODE为前三个字符组成的字符串，之后设置PARAMETER为第四个字符之后的字符串。
            //ui_data_len-5理解为ui_data_len-4-2+1，总长度 - 前四个字符 - 末尾\r\n + 1
            p_header_value = (char*)frmalloc(ui_data_len-5);
            if (p_header_value == NULL)
                goto ERROR;
            snprintf(p_header_value, ui_data_len-5, "%s", p_data+4);

            if (frreport_body_l7_add_str(p_st_body_data, APM_REPORT_KEY_RESP_CODE_STRING, p_header_value) == FR_FAIL)
                goto ERROR;
            frfree(p_header_value);
            p_header_value = NULL;
            p_st_private->i_flag_response = 0;
        }

        if (strncmp(p_data, "250 ", 4) == 0 && p_st_private->i_flag_data==1)
        {
            //p_st_private->i_flag_mail = 2;
            return APM_L7_ANALYZE_SET_RESP(APM_L7_ANALYZE_SUCC);
        }
        return APM_L7_ANALYZE_TODO;
    }

    return APM_L7_ANALYZE_SET_RESP(APM_L7_ANALYZE_SUCC);

ERROR:
    if (p_header_value)
        frfree(p_header_value);
    if (p_temp)
        frfree(p_temp);

    return APM_L7_ANALYZE_FAIL;
}

/*!
 * smtp session 数据上报
 *
 * \param p_st_body_data
 * \param p_st_private    (私有数据)
 *
 * \return APM_L7_ANALYZE_SUCC 成功\n
 *         APM_L7_ANALYZE_FAIL 失败\n
 *         APM_L7_ANALYZE_TODO 未结束
 *
 * \note
 *
 */
FR_STATIC int __smtp_session_report(Frreport_body_data *p_st_body_data, L7session_smtp_private *p_st_private)
{
    if (p_st_body_data == NULL || p_st_private == NULL)
        return FR_FAIL;

    if (frreport_body_l7_add_str(p_st_body_data, APM_REPORT_KEY_SESSION_USERNAME, p_st_private->p_user_name) == FR_FAIL)
        return FR_FAIL;

    if (frreport_body_l7_add_str(p_st_body_data, APM_REPORT_KEY_SESSION_ID, p_st_private->arr_session_id) == FR_FAIL)
        return FR_FAIL;

    if (frreport_body_l7_add_timeval(p_st_body_data, APM_REPORT_KEY_SESSION_LOGIN_TIMESTAMP, &p_st_private->st_login_timestamp) == FR_FAIL)
        return FR_FAIL;

    return FR_SUCC;
}

/*!
 * pop3的packet解析
 *
 * \param p_st_conn             (入力链接信息)
 * \param p_st_packet           (入力packet信息)
 * \param p_st_hash_report      (保存Frreport_data)
 * \param p_st_session          (私有数据)
 *
 * \return APM_L7_ANALYZE_SUCC   成功\n
 *         APM_L7_ANALYZE_FAIL   失败\n
 *         APM_L7_ANALYZE_TODO   数据未结束
 *
 * \note
 *
 */
FR_STATIC int __req_data_analysis(Conntrack *p_st_conn
                                 , Apm_packet *p_st_packet
                                 , Frhash_header *p_st_hash_report
                                 , L7_session *p_st_session)
{
    unsigned long ul_tmp = 0;
    int i_ret = APM_L7_ANALYZE_SUCC;
    L7session_smtp_private *p_st_private = NULL;
    L4_transaction *p_st_l4_trans = NULL;
    Frreport_data *p_st_report_data = NULL;

    if (p_st_conn == NULL || p_st_packet == NULL
        || p_st_hash_report == NULL || p_st_session == NULL)
        return APM_L7_ANALYZE_FAIL;

    p_st_private = (L7session_smtp_private*)p_st_session->p_session_private;
    p_st_l4_trans = &(p_st_private->st_l4_trans);

    //创建数据链表
    if (p_st_private->p_st_body_data_req == NULL)
    {
        p_st_private->p_st_body_data_req = frreport_body_new_l7(p_st_conn);
        if (p_st_private->p_st_body_data_req == NULL)
            goto ERROR;

        if (frreport_body_l7_add_connInfo(p_st_private->p_st_body_data_req, p_st_conn) == FR_FAIL)
            goto ERROR;

        //seq ++
        p_st_private->ul_seq = frct_seq_increase(p_st_conn);

        //st_request_time_first_byte
        p_st_l4_trans->st_request_time_first_byte.tv_sec = p_st_packet->st_timestamp.tv_sec;
        p_st_l4_trans->st_request_time_first_byte.tv_usec = p_st_packet->st_timestamp.tv_usec;
    }

    //请求的字节数
    p_st_l4_trans->l_request_bytes += p_st_packet->ui_payload_len;

    //req解析
    i_ret = __req_parse(p_st_packet->p_payload, p_st_packet->ui_payload_len, p_st_private, p_st_private->p_st_body_data_req, p_st_conn);
    if (i_ret == APM_L7_ANALYZE_FAIL || i_ret == APM_L7_ANALYZE_TODO || p_st_private->i_flag_mail != 1)
        return i_ret;

    if (frreport_body_l7_add_ulong(p_st_private->p_st_body_data_req, APM_REPORT_KEY_REQ_ID, p_st_private->ul_seq) == FR_FAIL)
        goto ERROR;

    //ResponseRequired
    if (frreport_body_l7_add_int(p_st_private->p_st_body_data_req, APM_REPORT_KEY_REQ_ISRESPREQUIRED, 1) == FR_FAIL)
        goto ERROR;

    //start time
    if (frreport_body_l7_add_timeval(p_st_private->p_st_body_data_req, APM_REPORT_KEY_REQ_START_TIMESTAMP, &p_st_l4_trans->st_request_time_first_byte) == FR_FAIL)
        goto ERROR;

    //st_request_time
    p_st_l4_trans->st_request_time.tv_sec = p_st_packet->st_timestamp.tv_sec;
    p_st_l4_trans->st_request_time.tv_usec = p_st_packet->st_timestamp.tv_usec;

    //send_time_us
    ul_tmp = frtime_diff(&(p_st_l4_trans->st_request_time_first_byte), &(p_st_l4_trans->st_request_time));
    if (frreport_body_l7_add_long(p_st_private->p_st_body_data_req, APM_REPORT_KEY_REQ_SEND_TIME_US, ul_tmp) == FR_FAIL)
        goto ERROR;

    //end time
    if (frreport_body_l7_add_timeval(p_st_private->p_st_body_data_req, APM_REPORT_KEY_REQ_END_TIMESTAMP, &p_st_l4_trans->st_request_time) == FR_FAIL)
        goto ERROR;

    //request bytes
    if (frreport_body_l7_add_long(p_st_private->p_st_body_data_req, APM_REPORT_KEY_REQ_DATA_BYTES, p_st_l4_trans->l_request_bytes) == FR_FAIL)
        goto ERROR;

    if (p_st_private->p_user_name)
    {
        if (__smtp_session_report(p_st_private->p_st_body_data_req, p_st_private) == FR_FAIL)
            goto ERROR;
    }

    p_st_report_data = frreport_data_l7_new(FRREPORT_TYPE_L7DATA_REQ);
    if (p_st_report_data == NULL)
        goto ERROR;

    //如果连续的两个request
    //拷贝相关链表
    p_st_private->p_lst_comm = FRREPORT_BODY_L7LIST_CLONE_COMM(p_st_private->p_st_body_data_req);
    if (p_st_private->p_lst_comm == NULL)
        goto ERROR;
    p_st_private->p_lst_req = FRREPORT_BODY_L7LIST_CLONE_REQ(p_st_private->p_st_body_data_req);
    if (p_st_private->p_lst_req == NULL)
        goto ERROR;

    if (frreport_data_add_body(p_st_report_data, p_st_private->p_st_body_data_req) == FR_FAIL)
        goto ERROR;
    p_st_private->p_st_body_data_req = NULL;

    if (frreport_data_hash(p_st_hash_report, p_st_report_data) == FR_FAIL)
        goto ERROR;

    return i_ret;

ERROR:
    if (p_st_private->p_st_body_data_req)
        frreport_body_free(&p_st_private->p_st_body_data_req);
    if (p_st_report_data)
        frreport_data_free(&p_st_report_data);
    if (p_st_private->p_lst_comm)
        frreport_body_l7list_free(&p_st_private->p_lst_comm);
    if (p_st_private->p_lst_req)
        frreport_body_l7list_free(&p_st_private->p_lst_req);
    return APM_L7_ANALYZE_FAIL;
}

/*!
 * pop3的packet解析
 *
 * \param p_st_conn             (入力链接信息)
 * \param p_st_packet           (入力packet信息)
 * \param p_st_hash_report      (保存Frreport_data)
 * \param p_st_session          (私有数据)
 *
 * \return APM_L7_ANALYZE_SUCC   成功\n
 *         APM_L7_ANALYZE_FAIL   失败\n
 *         APM_L7_ANALYZE_TODO   数据未结束
 *
 * \note
 *
 */
FR_STATIC int __resp_data_analysis(Conntrack *p_st_conn
                                 , Apm_packet *p_st_packet
                                 , Frhash_header *p_st_hash_report
                                 , L7_session *p_st_session)
{
    int i_ret = APM_L7_ANALYZE_SUCC;
    L7session_smtp_private *p_st_private = NULL;
    L4_transaction *p_st_l4_trans = NULL;
    unsigned long ul_tmp = 0;
    Frreport_data* p_st_report_data = NULL;

    if (p_st_conn == NULL || p_st_packet == NULL || p_st_hash_report == NULL || p_st_session == NULL)
        return APM_L7_ANALYZE_FAIL;

    p_st_private = (L7session_smtp_private*)p_st_session->p_session_private;
    p_st_l4_trans = &(p_st_private->st_l4_trans);

    if (p_st_l4_trans->st_response_time_first_byte.tv_sec == 0 &&
        p_st_l4_trans->st_response_time_first_byte.tv_usec == 0)
    {
        //创建数据链表
        p_st_private->p_st_body_data_resp = frreport_body_new_l7(p_st_conn);
        if (p_st_private->p_st_body_data_resp == NULL)
            goto ERROR;

        p_st_l4_trans->st_response_time_first_byte.tv_sec = p_st_packet->st_timestamp.tv_sec;
        p_st_l4_trans->st_response_time_first_byte.tv_usec = p_st_packet->st_timestamp.tv_usec;

        //start time
        if (frreport_body_l7_add_timeval(p_st_private->p_st_body_data_resp, APM_REPORT_KEY_RESP_START_TIMESTAMP, &p_st_l4_trans->st_response_time_first_byte) == FR_FAIL)
            goto ERROR;

        if (p_st_l4_trans->st_request_time_first_byte.tv_sec != 0 || p_st_l4_trans->st_request_time_first_byte.tv_usec != 0)
        {
            //server time
            ul_tmp = frtime_diff(&(p_st_l4_trans->st_request_time_first_byte), &(p_st_l4_trans->st_response_time_first_byte));
            if (frreport_body_l7_add_ulong(p_st_private->p_st_body_data_resp, APM_REPORT_KEY_RESP_SERVER_TIME_US, ul_tmp) == FR_FAIL)
                goto ERROR;
        }
        else
        {
            //如果不是第一个响应信息  并且没有req数据包  直接返回
            if (strncmp(p_st_packet->p_payload, "220 ", 4) != 0)
            {
                frreport_body_free(&p_st_private->p_st_body_data_resp);
                return APM_L7_ANALYZE_SET_RESP(APM_L7_ANALYZE_SUCC);
            }

            if (frreport_body_l7_add_ulong(p_st_private->p_st_body_data_resp, APM_REPORT_KEY_RESP_SERVER_TIME_US, 0) == FR_FAIL)
                goto ERROR;
        }
    }

    if (p_st_l4_trans->st_request_time_first_byte.tv_sec != 0)
    {
        FRREPORT_BODY_L7LIST_SET_REQ(p_st_private->p_st_body_data_resp, &p_st_private->p_lst_req);
        FRREPORT_BODY_L7LIST_SET_COMM(p_st_private->p_st_body_data_resp, &p_st_private->p_lst_comm);
    }
    else
    {
        if (frreport_body_l7_add_connInfo(p_st_private->p_st_body_data_resp, p_st_conn) == FR_FAIL)
            goto ERROR;
    }

    //请求的字节数
    p_st_l4_trans->l_response_bytes += p_st_packet->ui_payload_len;

    //resp解析
    i_ret = __resp_parse(p_st_packet->p_payload, p_st_packet->ui_payload_len, p_st_private, p_st_private->p_st_body_data_resp, p_st_conn);
    if (i_ret == APM_L7_ANALYZE_FAIL || i_ret == APM_L7_ANALYZE_TODO)
        return i_ret;
    if (!p_st_private->i_flag_mail)
    {
		    switch(p_st_private->i_flag_data_type)
		    {
		        case 2:
				        p_st_report_data = frreport_update_create(p_st_conn, APM_REPORT_UPDATE_LOGON, p_st_private->p_user_name, &p_st_packet->st_timestamp);
				        if (p_st_report_data)
				        {
				            if (frreport_send(g_p_apm_context, &p_st_report_data) == FR_FAIL)
				                return FR_FAIL;
				        }
				        i_ret = APM_L7_ANALYZE_SET_LOGIN(i_ret);
				        break;
		        case 3:
				        p_st_report_data = frreport_update_create(p_st_conn, APM_REPORT_UPDATE_LOGON_REJECT, p_st_private->p_user_name, &p_st_packet->st_timestamp);
				        if (p_st_report_data)
				        {
				            if (frreport_send(g_p_apm_context, &p_st_report_data) == FR_FAIL)
				                return FR_FAIL;
				        }
				        i_ret = APM_L7_ANALYZE_SET_LOGIN(i_ret);
				        break;
		        case 5:
				        p_st_report_data = frreport_update_create(p_st_conn, APM_REPORT_UPDATE_LOGOUT, p_st_private->p_user_name, &p_st_packet->st_timestamp);
				        if (p_st_report_data)
				        {
				            if (frreport_send(g_p_apm_context, &p_st_report_data) == FR_FAIL)
				                return FR_FAIL;
				        }
				        i_ret = APM_L7_ANALYZE_SET_LOGOUT(i_ret);
				        break;
		        default:
		            break;
		    }
        __smtp_session_reset((void *)(p_st_private));
        return i_ret;
    }

    //数据上报
    //st_response_time
    p_st_l4_trans->st_response_time.tv_sec = p_st_packet->st_timestamp.tv_sec;
    p_st_l4_trans->st_response_time.tv_usec = p_st_packet->st_timestamp.tv_usec;

    if (frreport_body_l7_add_long(p_st_private->p_st_body_data_resp, APM_REPORT_KEY_RESP_DATA_BYTES, p_st_l4_trans->l_response_bytes) == FR_FAIL)
        goto ERROR;

    //down load time
    ul_tmp = frtime_diff(&(p_st_l4_trans->st_response_time_first_byte), &(p_st_l4_trans->st_response_time));
    if (frreport_body_l7_add_long(p_st_private->p_st_body_data_resp, APM_REPORT_KEY_RESP_DOWNLOAD_TIME_US, ul_tmp) == FR_FAIL)
        goto ERROR;

    //end time
    if (frreport_body_l7_add_timeval(p_st_private->p_st_body_data_resp, APM_REPORT_KEY_RESP_END_TIMESTAMP, &p_st_l4_trans->st_response_time) == FR_FAIL)
        goto ERROR;

    p_st_report_data = frreport_data_l7_new(FRREPORT_TYPE_L7DATA_RESP);
    if (p_st_report_data == NULL)
        goto ERROR;

    if (frreport_data_add_body(p_st_report_data, p_st_private->p_st_body_data_resp) == FR_FAIL)
        goto ERROR;
    p_st_private->p_st_body_data_resp = NULL;

    if (frreport_data_hash(p_st_hash_report, p_st_report_data) == FR_FAIL)
        goto ERROR;
    p_st_report_data = NULL;

    __smtp_session_reset((void *)(p_st_private));
    return i_ret;

ERROR:
    if (p_st_report_data)
        frreport_data_free(&p_st_report_data);
    if (p_st_private->p_st_body_data_resp)
        frreport_body_free(&p_st_private->p_st_body_data_resp);
    return APM_L7_ANALYZE_FAIL;
}

/*!
 * smtp的packet解析
 *
 * \param p_st_conn             (入力链接信息)
 * \param p_st_packet           (入力packet信息)
 * \param p_st_hash_report      (保存Frreport_data)
 *
 * \return APM_L7_ANALYZE_SUCC   成功\n
 *         APM_L7_ANALYZE_FAIL   失败
 *         APM_L7_ANALYZE_TODO   数据未结束
 *
 * \note
 *
 */
int smtp_analysis(Conntrack *p_st_conn
                  , Apm_packet *p_st_packet
                  , Frhash_header *p_st_hash_report)
{
    int i_ret = APM_L7_ANALYZE_SUCC;
    L7_session *p_st_session = NULL;

    if (p_st_conn == NULL || p_st_packet == NULL || p_st_hash_report == NULL)
        return APM_L7_ANALYZE_FAIL;

    //到Conntrack中取得session信息
    p_st_session = frct_get_session(p_st_conn);
    if (p_st_session == NULL)
        return APM_L7_ANALYZE_FAIL;

    //fin syn包销毁私有数据
    if (p_st_packet->em_analyz_cmd == FRANALYZ_CMD_FIN || p_st_packet->em_analyz_cmd == FRANALYZ_CMD_SYN)
    {
        smtp_session_destroy((void *)(p_st_session->p_session_private));
        p_st_session->p_session_private = NULL;
        return APM_L7_ANALYZE_SUCC;
    }

    //p_session_private分配内存
    if (p_st_session->p_session_private == NULL)
    {
        p_st_session->p_session_private = __smtp_private_create();
        //内存分配失败
        if (p_st_session->p_session_private == NULL)
            return APM_L7_ANALYZE_FAIL;
    }

    //request
    if (p_st_packet->i_direction == FR_DIRECT_CLIENT2SRV)
    {
        i_ret = __req_data_analysis(p_st_conn,p_st_packet,p_st_hash_report,p_st_session);
        if (i_ret == APM_L7_ANALYZE_FAIL)
            goto ERROR;

        return i_ret;
    }
    //response
    else
    {
        i_ret = __resp_data_analysis(p_st_conn,p_st_packet,p_st_hash_report, p_st_session);
        if (i_ret == APM_L7_ANALYZE_FAIL)
            goto ERROR;

        return i_ret;
    }

ERROR:
    if (p_st_session->p_session_private)
        smtp_session_destroy(p_st_session->p_session_private);
    p_st_session->p_session_private = NULL;
    return APM_L7_ANALYZE_FAIL;
}

/*!
 * 私有信息销毁
 *
 * \param p_private_session  (smtp的私有session数据)
 *
 * \return FR_SUCC   成功\n
 *
 * \note
 *
 */
int smtp_session_destroy(void *p_private_session)
{
    L7session_smtp_private *p_st_private = (L7session_smtp_private*)p_private_session;

    if (p_st_private)
    {
        if (p_st_private->p_st_body_data_req)
            frreport_body_free(&p_st_private->p_st_body_data_req);

        if (p_st_private->p_st_body_data_resp)
            frreport_body_free(&p_st_private->p_st_body_data_resp);

        if (p_st_private->p_lst_req)
            frreport_body_l7list_free(&p_st_private->p_lst_req);

        if (p_st_private->p_lst_comm)
            frreport_body_l7list_free(&p_st_private->p_lst_comm);

        if (p_st_private->p_header_name)
            frfree(p_st_private->p_header_name);

        if (p_st_private->p_user_name)
            frfree(p_st_private->p_user_name);

        frfree(p_st_private);
    }
    return FR_SUCC;
}